<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2020/3/7
 * Time: 13:46
 */
namespace Xtsb\Cims\Push;
use Xtsb\Cims\Exception\ApiErrorDesc;
use Xtsb\Cims\Exception\ApiException;
use crypt\thirdPartyCrypt;

/**
 * Class Accessnew 内部系统之间进行加密交互的类，基本实现流程:
 * A系统要向B系统发出数据请求
 * 1、 A系统，使用B系统对应的公钥进行加密，发送加密后的数据
 * 2、 B系统，接收A系统发送过来的密文，用B系统自身的私钥进行解密
 * 3、 B系统根据接收到的数据，进行业务处理
 * 4、 B系统处理完毕的结果，采用A系统对应的公钥进行加密，发送加密后的数据
 * 5、 A系统，接收B系统发送过来的密文，用A系统自身的公钥进行解密
 * 6、 A系统根据返回的数据，进行业务处理
 * @package app\index\controller
 */
class WechatPush
{
  /**
   * 平台内部系统之间的交互
   * url: 校验对方的url是否正确
   * ip: 校验对方的源ip是否正确，做软件层级的ip白名单
   * filename: 根据对方的平台名称，获取对应的秘钥进行数据解密
   * platform_code: 解密后，校验平台的标识符是否正确
   **/
  private $config = [
    //生产环境配置，若有对应系统对接，请向上级获取对应的秘钥，部署到服务器上，部署完毕之后删除本地备份
    'cims' => ['ip' => [], 'encrypt_file' => 'xtsb_cims', 'url' => 'http://cims.chetell.com', 'platform_code' => 'goqn450vdr7dr_4gq8tbvi1rr4ufu08j'],
    'service' => ['ip' => [], 'encrypt_file' => 'xtsb_service', 'url' => 'http://service.chetell.com', 'platform_code' => 'rdve98vrlnmro9ltu56dg4ubj8uv84h'],
    //测试环境配置，除了保险系统之外，不要上到生产环境; 对应的秘钥对请放置到config文件夹下
    'cims_dev' => ['ip' => [], 'encrypt_file' => 'xtsb_cims_dev', 'url' => 'http://cims_api_dev.chetell.com', 'platform_code' => 'goqn450vdr7dr_4gq8tbvi1rr4ufu08j'],

  ];
  //定义当前的平台名称
  private $my_platform = 'cims';//正式地址
  private $my_config = [];
  private $peer_platform = '';
  private $peer_config = [];
  private $encrypt_instance;
  private $decrypt_instance;
  private $result_page = 0; //是否显示结果页面 0：不显示，1：显示
  public $error_code = [
    '00000' => '执行成功',
    '00001' => '尚未关注公众号，微信通知消息推送失败',
    '00002' => '平台名称不正确',
    '00003' => '平台标志不正确',
    '00004' => 'action字段值不正确',
    '43004' => '微信公众号通知失败：不存在openid',
    '10000' => '未知错误'
  ];

  //初始化
  public function __construct($platform = null, $result_page = 0)
  {
    if (!$platform) {
      $platform = input('platform', 'service');
    }
    $this->platformCheck($platform, null);
    $this->result_page = $result_page;
    $this->peer_platform = $platform;
    $temp = $this->config;
    $this->my_config = $temp[$this->my_platform];
    $this->peer_config = $temp[$this->peer_platform];
    $this->encrypt_instance = new thirdPartyCrypt($this->peer_config['encrypt_file']);
    $this->decrypt_instance = new thirdPartyCrypt($this->my_config['encrypt_file']);
  }

  /**
   * 向其他系统发送加密请求，并将返回数据进行解密，
   * @param $tempArray  请求的字段数据,将会被编译成json并加密发出
   * @param $url_append string 请求对方的url的action, 即路由中定义的，默认
   * @return mixed
   */
  public function encryptRequest($tempArray = [], $url_append = '/api')
  {
    $tempArray['platform_code'] = $this->my_config['platform_code'];
    $tempArray['timestamp'] = time();
    $url = $this->peer_config['url'] . $url_append;
    $encrypted = $this->encrypt_instance->long_encrypt(json_encode(array('data' => $tempArray), JSON_UNESCAPED_UNICODE));
    $result = $this->postData($encrypted, $url . '?platform=' . $this->my_platform);
    return $this->decryptData($result);
  }

  /**
   * 响应其他系统的请求，并且用对方的公钥加密，并返回数据
   * @param $tempArray  返回的数据数组
   */
  public function encryptResponse($tempArray = [])
  {
    $tempArray['platform_code'] = $this->my_config['platform_code'];
    $tempArray['timestamp'] = time();
    $json_data = json_encode(array('code' => '00000', 'data' => $tempArray), JSON_UNESCAPED_UNICODE);
    $encrypted = $this->encrypt_instance->long_encrypt($json_data);
    echo $encrypted;
    die;
  }

  /**
   * 响应本系统的一些需求，跳转至其他系统的页面，例如OA与ERP之间的跳转， 或者OA/ERP与保险系统的跳转等
   * @param $tempArray
   * @param string $url_append
   */
  public function encryptResponseJump($tempArray = [], $url_append = '/api')
  {
    $tempArray['platform_code'] = $this->my_config['platform_code'];
    $tempArray['timestamp'] = time();
    $url = $this->peer_config['url'] . $url_append;
    $platform = $this->my_platform;
    $json_data = json_encode(array('data' => $tempArray), JSON_UNESCAPED_UNICODE);
    $encrypted = $this->encrypt_instance->long_encrypt($json_data);
    echo <<<EOT
            <form name='fr' action='{$url}' method='POST'>
                <input type='hidden' name='platform' value='{$platform}'>
                <input type='hidden' name='data' value='{$encrypted}'>
            </form>
            <script type='text/javascript'>
                document.fr.submit();
            </script>
EOT;
    die;
  }

  /**
   * 出现错误的情况，返回应答的错误码信息
   * @param $error_code 传入对应的错误码，自动将错误码信息进行加密返回
   */
  public function encryptResponseFail($error_code, $message = '')
  {
    $tempArray['message'] = $message ? $message : (isset($this->error_code[$error_code]) ? $this->error_code[$error_code] : $this->error_code['10000']);
    $tempArray['platform_code'] = $this->my_config['platform_code'];
    $tempArray['timestamp'] = time();
    $json_data = json_encode(array('code' => $error_code, 'data' => $tempArray), JSON_UNESCAPED_UNICODE);
    $encrypted = $this->encrypt_instance->long_encrypt($json_data);
    echo $encrypted;
    die;
  }

  //接收加密数据
  public function decryptData($data = null)
  {
    if (!$data)
      $data = input('data', null);
    if (!$data)
      $data = file_get_contents("php://input");
    $decryptData = json_decode($this->decrypt_instance->long_decrypt($data), true);
    $code = isset($decryptData['data']['platform_code']) ? $decryptData['data']['platform_code'] : '';
    $this->platformCheck($this->peer_platform, $code);
    return $decryptData;
  }

  /**
   * 平台接入检查
   * @param $platform  平台名称
   * @param $code  平台的内部标志码, 若当前有值，默认检查对应的标志码是否正确
   * @return mixed
   */
  private function platformCheck($platform, $code)
  {
    $list = $this->config;

    //平台名称不正确，可以用明文返回，因为此时系统无法找到对应的公钥进行加密，其他情况均需要加密返回
    if (!key_exists($platform, $list)) {
      throw new ApiException(ApiErrorDesc::ERROR_DEFAULT, $this->error_code['00002']);//
    }

    if ($code) {
      if ($this->result_page) {
        if ($code != $list[$platform]['platform_code'])
          throw new ApiException(ApiErrorDesc::ERROR_DEFAULT, $this->error_code['00003']);
      } else {
        if ($code != $list[$platform]['platform_code']) {
          $this->encryptResponseFail('00003');
        } else
          return true;
      }
    }
  }

  public function getPeerInfo()
  {
    return ['platform' => $this->peer_platform, 'url' => $this->peer_config['url']];
  }

  //post提交并返回结果
  public function postData($postData, $url)
  {

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 300);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, false);//表示需要response header
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $file_contents = curl_exec($ch);
    curl_close($ch);
    return $file_contents;
  }

}
